﻿package 
{
	import com.flashandmath.dg.bitmapUtilities.BitmapSaver;
	import PNGEncoder;
	import flash.geom.Point;
	import flash.display.Bitmap;
	import flash.display.Sprite;
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.DisplayObject;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.text.TextFieldType;
	import flash.text.TextFieldAutoSize;
	import flash.display.BitmapData;
	import flash.geom.ColorTransform;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.ByteArray;
	import flash.net.FileReference;
	import flash.net.FileFilter;
	
	import flash.ui.Mouse;
	import fl.events.SliderEvent; 
	import flash.geom.Matrix;
	import flash.filters.BlurFilter;
	import flash.geom.Rectangle;
	import flash.display.PixelSnapping;
	import flash.geom.Transform;
	import flash.utils.Timer;
	
	public class Main extends MovieClip
	{
		// this is hard coded now but we should be able to get the screen size and width
		//private static const _MAX_WIDTH		: Number = board.width;
		//private static const _MAX_HEIGHT	: Number = board.height;

		// smoothing properties
		var swatchColors:Vector.<uint>;
		var lineLayer:Sprite;
		var lastSmoothedMouseX:Number;
		var lastSmoothedMouseY:Number;
		var lastMouseX:Number;
		var lastMouseY:Number;
		var lastThickness:Number;
		var lastRotation:Number;
		var lineColor:uint;
		var lineThickness:Number;
		var lineRotation:Number;
		var L0Sin0:Number;
		var L0Cos0:Number;
		var L1Sin1:Number;
		var L1Cos1:Number;
		var sin0:Number;
		var cos0:Number;
		var sin1:Number;
		var cos1:Number;
		var dx:Number;
		var dy:Number;
		var dist:Number;
		var targetLineThickness:Number;
		var colorLevel:Number;
		var targetColorLevel:Number;
		var smoothedMouseX:Number;
		var smoothedMouseY:Number;
		var tipLayer:Sprite;
		var boardBitmap:Bitmap;
		var boardBitmapData:BitmapData;
		var bitmapHolder:Sprite;
		var boardWidth:Number;
		var boardHeight:Number;
		var smoothingFactor:Number;
		var mouseMoved:Boolean;
		var dotRadius:Number;
		var startX:Number;
		var startY:Number;
		var undoStack:Vector.<BitmapData>;
		var minThickness:Number;
		var thicknessFactor:Number;
		var thicknessSmoothingFactor:Number;
		var mouseChangeVectorX:Number;
		var mouseChangeVectorY:Number;
		var lastMouseChangeVectorX:Number;
		var lastMouseChangeVectorY:Number;
		
		var controlVecX:Number;
		var controlVecY:Number;
		var controlX1:Number;
		var controlY1:Number;
		var controlX2:Number;
		var controlY2:Number;
		
		var tipTaperFactor:Number;
		
		var numUndoLevels:Number;
		
		//var controlPanel:Sprite;
		var paintColorR1:Number;
		var paintColorG1:Number;
		var paintColorB1:Number;
		var paintColorR2:Number;
		var paintColorG2:Number;
		var paintColorB2:Number;
		
		var red:Number;
		var green:Number;
		var blue:Number;
		
		var colorChangeRate:Number;
		
		var panelColor:uint;
		
		var boardMask:Sprite;
		
		// Dictates which menu should be shown on screen.
		var menuMode:Number;	
		
		// Undo bitmap storage.
		var bitmapUndo:BitmapData;
		var bitmapArray:Array = new Array();
		var currentBitmap:Number = 1;
		var maxSeenBitmap:Number = 1;
		var videoStopped:Boolean = false;
		var transitionArray:Array = new Array();
		var tempVarForTransitions:Number;

		// crop rectangle
		const croppingRect:Sprite = new Sprite(); 
		var _dragging:Boolean = false; 
		var _corner:Point; 

		// croping properties
		var scaleValue:Number = 1;
		
		// saving options
		var bitmapSaver:BitmapSaver;
		
		/* Variables */

		/* Pencil Tool shape, everything drawed with this tool and eraser is stored inside board.pencilDraw */

		var pencilDraw:Shape = new Shape();

		/* Text format */

		var textformat:TextFormat = new TextFormat();

		/* Colors */

		var colorsBmd:BitmapData;
		var pixelValue:uint;
		var activeColor:uint = 0x000000;

		/* Save dialog instance */

		var saveDialog:SaveDialog;

		/* Active var, to check wich tool is active */

		var active:String;

		/* Shape size color */

		var ct:ColorTransform = new ColorTransform();
		
		private var fileFilter				: FileFilter;
		private var fileRef				: FileReference;
		private var loader					: Loader;
		private var bitmap					: Bitmap;
		
		private var myCursor : MovieClip;
		public function Main():void
		{											
			boardWidth = board.width; //_MAX_WIDTH;
			boardHeight = board.height; //_MAX_HEIGHT;
				
			minThickness = 0.2;
			thicknessFactor = 0.25;
			
			smoothingFactor = 0.3;  //Should be set to something between 0 and 1.  Higher numbers mean less smoothing.
			thicknessSmoothingFactor = 0.3;
			
			dotRadius = 2; //radius for drawn dot if there is no mouse movement between mouse down and mouse up.
			
			tipTaperFactor = 0.8;
			
			numUndoLevels = 10;
			
			colorChangeRate = 0.05;
			
			panelColor = 0xAAAAAA;
			
			paintColorR1 = 16;
			paintColorG1 = 0;
			paintColorB1 = 0;
			paintColorR2 = 128;
			paintColorG2 = 0;
			paintColorB2 = 0;
			
			var color_transform:ColorTransform=eyeColor.transform.colorTransform;
			color_transform.color=activeColor;			
			eyeColor.transform.colorTransform = color_transform;
			
			//The undo buffer will hold the previous drawing.
			//If we want more levels of undo, we would have to record several undo buffers.  We only use one
			//here for simplicity.
			undoStack = new Vector.<BitmapData>;
	
			// Set up the multiple bitmaps.
			var i:int;
			for (i = 0; i < 10; i++)
		    {
				bitmapArray[i] = new BitmapData(boardWidth, boardHeight, false);
				transitionArray[i] = 0;
			}
			boardBitmapData = bitmapArray[1];
			
			bitmapSaver = new BitmapSaver(boardBitmapData);
			bitmapSaver.x = 0.5*(boardWidth - bitmapSaver.width);
			bitmapSaver.y = 0.5*(boardHeight - bitmapSaver.height);
			
			//boardBitmapData = new BitmapData(boardWidth, boardHeight, false);
			bitmapUndo = new BitmapData(boardWidth, boardHeight, false);
			boardBitmap = new Bitmap(boardBitmapData);
			
			
			
				
			//The undo buffer will hold the previous drawing.
			//If we want more levels of undo, we would have to record several undo buffers.  We only use one
			//here for simplicity.
			//undoStack = new Vector.<BitmapData>;
			bitmapHolder = new Sprite();
			lineLayer = new Sprite();
			
			boardMask = new Sprite();
			boardMask.graphics.beginFill(0xFF0000);
			boardMask.graphics.drawRect(0,0,boardWidth,boardHeight);
			boardMask.graphics.endFill();
			
			//drawBackground();
			
			/*
			The tipLayer holds the tip portion of the line.
			Because of the smoothing technique we are using, while the user is drawing the drawn line will not
			extend all the way from the last position to the current mouse position.  We use a small 'tip' to 
			complete this line all the way to the current mouse position.
			*/
			tipLayer = new Sprite();
			tipLayer.mouseEnabled = false;
			
			/*
			Bitmaps cannot receive mouse events.  so we add it to a holder sprite.
			*/
			
			board.addChild(bitmapHolder);
			bitmapHolder.x = 5;
			bitmapHolder.y = 5;
			bitmapHolder.addChild(boardBitmap);
			bitmapHolder.addChild(tipLayer);
			bitmapHolder.addChild(boardMask);
			bitmapHolder.mask = boardMask;
			
			// other properties
			textformat.font = "Quicksand Bold Regular";
			textformat.bold = true;
			textformat.size = 16;

			fileFilter = new FileFilter ( "Image", "*.jpg;*.jpeg;*.gif;*.png;" ) ;	
			
			convertToBMD();

			addListeners();

			/* Hide tools highlights */

			//pencil.visible = false;	
			crop.visible = false;
			hideTools(browser, eyedrop);
			hideTools(pencil, brush);
			hideTools(eraser, txt);
			
			// this creates an instance of the library MovieClip with the
			// name, "MyCursorClass".  this contains your mouse cursor art
			//
			//myCursor = new PencilCursor();
			
			//toolsPanel.addChild(pencil);
			//toolsPanel.addChild(eraser);
			
			// Preload the transitions in the combo box.
			transitionBox.prompt = "Transition";
			transitionBox.addItem( {label: "None"} );
			transitionBox.addItem( {label: "Static"} );
			transitionBox.addItem( {label: "Wipe right"} );
			transitionBox.addItem( {label: "Wipe down"} );
		}

		
		private function follow(evt:MouseEvent){			
			if (board.hitTestPoint(mouseX,mouseY,true)) {
				//hitTest.text = "on board";
				Mouse.hide();
				addChild(myCursor);
				myCursor.visible = true;
				myCursor.x = mouseX + myCursor.width/2;
				myCursor.y = mouseY; // - myCursor.height/2;
			}
			
			else {
				//hitTest.text = "off board";
				myCursor.visible = false;		
				Mouse.show();				
			}
		}
		
		private function BrushTool(e:MouseEvent):void
		{
			
			
			// you'll want to make sure the child is added above everything
			// else, possibly in its own container
			myCursor = new BrushCursor();			
			//addChild(myCursor);
	
			stage.addEventListener(MouseEvent.MOUSE_MOVE,follow);

			/* Quit active tool */

			quitActiveTool();

			/* Set to Active */

			active = "Brush";

			/* Listeners */
			
			bitmapHolder.addEventListener(MouseEvent.MOUSE_DOWN, startDraw);


			/* Highlight */

			highlightTool(brush);
			hideTools(eyedrop, crop);
			hideTools(pencil, browser);
			hideTools(eraser, txt);
		}
		private function PencilTool(e:MouseEvent):void
		{
			
			
			// you'll want to make sure the child is added above everything
			// else, possibly in its own container
			myCursor = new PencilCursor();			
			//addChild(myCursor);
	
			stage.addEventListener(MouseEvent.MOUSE_MOVE,follow);

			/* Quit active tool */

			quitActiveTool();

			/* Set to Active */

			active = "Pencil";

			/* Listeners */

			board.addEventListener(MouseEvent.MOUSE_DOWN, startPencilTool);
			board.addEventListener(MouseEvent.MOUSE_UP, stopPencilTool);			

			/* Highlight */

			highlightTool(pencil);			
			hideTools(eyedrop, crop);
			hideTools(brush, browser);			
			hideTools(eraser, txt);

			ct.color = activeColor;
			shapeSize.transform.colorTransform = ct;
		}

		private function startPencilTool(e:MouseEvent):void
		{
			pencilDraw = new Shape();
			//board.addChild(pencilDraw);						
			//boardBitmapData.draw(pencilDraw);
			pencilDraw.graphics.moveTo(this.mouseX, this.mouseY);
			pencilDraw.graphics.lineStyle(shapeSize.width, activeColor);

			board.addEventListener(MouseEvent.MOUSE_MOVE, drawPencilTool);			
		}

		private function drawPencilTool(e:MouseEvent):void
		{
			pencilDraw.graphics.lineTo(mouseX, mouseY);
			boardBitmapData.draw(pencilDraw);
		}

		private function stopPencilTool(e:MouseEvent):void
		{
			board.removeEventListener(MouseEvent.MOUSE_MOVE, drawPencilTool);			
									
			//record undo bitmap and add to undo stack			
			var undoBuffer:BitmapData = new BitmapData(boardWidth, boardHeight, false);
			undoBuffer.copyPixels(boardBitmapData,undoBuffer.rect,new Point(0,0));
			undoStack.push(undoBuffer);
			if (undoStack.length > numUndoLevels + 1) {
				undoStack.splice(0,1);
			}	
		}

		/* Eraser Tool */

		private function EraserTool(e:MouseEvent):void
		{		
			// you'll want to make sure the child is added above everything
			// else, possibly in its own container
			myCursor = new EraserCursor();			
			//addChild(myCursor);
	
			stage.addEventListener(MouseEvent.MOUSE_MOVE,follow);
						
			/* Quit active tool */

			quitActiveTool();
			
			// you'll want to make sure the child is added above everything
			// else, possibly in its own container
			//myCursor = new EraserCursor();			
			//addChild(myCursor);	
			//stage.addEventListener(MouseEvent.MOUSE_MOVE,follow);

			/* Set to Active */

			active = "Eraser";

			/* Listeners */

			board.addEventListener(MouseEvent.MOUSE_DOWN, startEraserTool);
			board.addEventListener(MouseEvent.MOUSE_UP, stopEraserTool);

			/* Highlight */

			highlightTool(eraser);
			hideTools(brush, browser);						
			hideTools(pencil, txt);
			hideTools(eyedrop, crop);

			ct.color = 0x000000;
			shapeSize.transform.colorTransform = ct;
		}

		private function startEraserTool(e:MouseEvent):void
		{
			pencilDraw = new Shape();
			//board.addChild(pencilDraw);

			pencilDraw.graphics.moveTo(mouseX, mouseY);
			pencilDraw.graphics.lineStyle(shapeSize.width, 0xFFFFFF);

			board.addEventListener(MouseEvent.MOUSE_MOVE, drawEraserTool);
		}

		private function drawEraserTool(e:MouseEvent):void
		{
			pencilDraw.graphics.lineTo(mouseX, mouseY);
			boardBitmapData.draw(pencilDraw);
		}

		function stopEraserTool(e:MouseEvent):void
		{
			board.removeEventListener(MouseEvent.MOUSE_MOVE, drawEraserTool);

		}

		/* Text Tool */

		private function TextTool(e:MouseEvent):void
		{
			/* Quit active tool */

			quitActiveTool();

			/* Set to Active */

			active = "Text";

			/* Listener */

			board.addEventListener(MouseEvent.MOUSE_UP, writeText);

			/* Highlight */

			highlightTool(txt);
			hideTools(eyedrop, crop);
			hideTools(pencil, brush);
			hideTools(browser, eraser);
		}

		private function writeText(e:MouseEvent):void
		{
			var textfield = new TextField();

			textfield.type = TextFieldType.INPUT;
			textfield.autoSize = TextFieldAutoSize.LEFT;
			textfield.selectable = false;			
			textfield.defaultTextFormat = textformat;
			textfield.textColor = activeColor;
			textfield.x = mouseX;
			textfield.y = mouseY;
			stage.focus = textfield;

			board.addChild(textfield);
		}

		/* Save */

		private function export():void
		{			
			/* Listeners */
			var bmd:BitmapData = new BitmapData(boardWidth, boardHeight);
			bmd.draw(board);						
			var originalBitmap:Bitmap = BitmapScaled(bmd);
			
			bitmapSaver.bitmapDataToSave = originalBitmap.bitmapData;			
			addChild(bitmapSaver);
			bitmapSaver.addEventListener(BitmapSaver.BUTTON_CLICKED, closeWindow);														
			/*
			//var bmd:BitmapData = new BitmapData(_MAX_WIDTH, _MAX_HEIGHT);
			var bmd:BitmapData = new BitmapData(boardWidth, boardHeight);
			bmd.draw(board);						
			var originalBitmap:Bitmap = BitmapScaled(bmd);
			
			var ba:ByteArray = PNGEncoder.encode(originalBitmap.bitmapData);

			var file:FileReference = new FileReference();

			file.addEventListener(Event.COMPLETE, saveSuccessful);

			file.save(ba, "MyDrawing.jpeg");
			*/
		}
		
		function BitmapScaled(bigBMD:BitmapData):Bitmap{
			var scale:Number = scaleValue;
			var matrix:Matrix = new Matrix();
			matrix.scale(scale, scale);
			
			var smallBMD:BitmapData = new BitmapData(bigBMD.width * scale, bigBMD.height * scale, true, 0x000000);
			smallBMD.draw(bigBMD, matrix, null, null, null, true);
			
			var bitmap:Bitmap = new Bitmap(smallBMD, PixelSnapping.NEVER, true);
			return bitmap;
		}

		function closeWindow(evt:Event):void {
			removeChild(bitmapSaver);
			bitmapSaver.removeEventListener(BitmapSaver.BUTTON_CLICKED, closeWindow);			
		}
		/*
		private function saveSuccessful(e:Event):void
		{
			bitmapSaver.removeEventListener(BitmapSaver.EVENT_COMPLETED, saveSuccessful);
			saveDialog = new SaveDialog();

			addChild(saveDialog);

			saveDialog.closeBtn.addEventListener(MouseEvent.MOUSE_UP, closeSaveDialog);
		}

		private function closeSaveDialog(e:MouseEvent):void
		{
			removeChild(saveDialog);
		}
		*/
		private function save(e:MouseEvent):void
		{									
			stage.removeEventListener(MouseEvent.MOUSE_MOVE,follow);
			
			/* Quit active tool */
			quitActiveTool();
			
			myCursor.visible = false;
			Mouse.show();
			
			/* Set to Active */

			active = "Save";
			
			export();
			
			/* Highlight */
			//highlightTool(save);
			browser.visible = false;
			hideTools(eyedrop, crop);
			hideTools(pencil, brush);
			hideTools(eraser, txt);
		}

		/* Clear Tool */

		private function clearBoard(e:MouseEvent):void
		{
			/* Create a blank rectangle on top of everything but board */

			var blank:Shape = new Shape();

			blank.graphics.beginFill(0xFFFFFF);
			blank.graphics.drawRect(0, 0, board.width, board.height);
			blank.graphics.endFill();

			//board.addChild(blank);			
			boardBitmapData.draw(blank);
			
		}

		/* Default colors function */

		private function convertToBMD():void
		{
			colorsBmd = new BitmapData(colors.width,colors.height);
			colorsBmd.draw(colors);
		}

		private function chooseColor(e:MouseEvent):void
		{
			pixelValue = colorsBmd.getPixel(colors.mouseX,colors.mouseY);
			activeColor = pixelValue;//uint can be RGB!
			
			ct.color = activeColor;
			shapeSize.transform.colorTransform = ct;
			
			// eye drop color
			var color_transform:ColorTransform=eyeColor.transform.colorTransform;
			color_transform.color=activeColor;			
			eyeColor.transform.colorTransform = color_transform;
			
			// brush color
			var R1:Number = activeColor >> 16 & 0xFF;
			var G1:Number = activeColor >> 8  & 0xFF;
			var B1:Number = activeColor & 0xFF;

			// need R2,G2,B2 for gradient color

			paintColorR1 = R1;
			paintColorG1 = G1;
			paintColorB1 = B1;
			paintColorR2 = 128;
			paintColorG2 = 0;
			paintColorB2 = 0;			
		}

		/* Quit active function */

		private function quitActiveTool():void
		{
			switch (active)
			{
				case "Pencil" :
					board.removeEventListener(MouseEvent.MOUSE_DOWN, startPencilTool);
					board.removeEventListener(MouseEvent.MOUSE_UP, stopPencilTool);
					break;
				case "Eraser" :
					board.removeEventListener(MouseEvent.MOUSE_DOWN, startEraserTool);
					board.removeEventListener(MouseEvent.MOUSE_UP, stopEraserTool);
					break;
				case "Text" :
					board.removeEventListener(MouseEvent.MOUSE_UP, writeText);
					break;
				case "Brush" :
					bitmapHolder.removeEventListener(MouseEvent.MOUSE_DOWN, startDraw);	
					break;
				case "EyeDrop" :
					board.removeEventListener(MouseEvent.CLICK, EyeDropClicked);	
					break;
				case "Crop" :				
					stage.removeEventListener(MouseEvent.MOUSE_DOWN, setAnchor);
					break;
				default :
			}
		}
		
		/* Highlight active Tool */

		private function highlightTool(tool:DisplayObject):void
		{
			tool.visible=true;
		}

		private function hideTools(tool1:DisplayObject, tool2:DisplayObject):void
		{
			tool1.visible=false;
			tool2.visible=false;
		}

		/* Change shape size */

		private function changeShapeSize(e:MouseEvent):void
		{
			if (shapeSize.width >= 50)
			{
				shapeSize.width = 1;
				shapeSize.height = 1;

				/* TextFormat */

				textformat.size = 16;
			}
			else
			{
				shapeSize.width += 5;
				shapeSize.height=shapeSize.width;

				/* TextFormat */

				textformat.size+=5;
			}
		}
		
		private function BrowserTool ( evt : MouseEvent ) : void
		{			
			/* Quit active tool */

			quitActiveTool();

			/* Set to Active */

			active = "Browser";

			/* Listener */
			fileRef = new FileReference ( ) ;
			fileRef.browse ( [fileFilter] ) ;
			fileRef.addEventListener ( Event.SELECT, onImageSelect ) ;


			/* Highlight */
			highlightTool(browser);
			hideTools(eyedrop, crop);
			hideTools(pencil, brush);
			hideTools(eraser, txt);
			
		}
		
		private function onImageSelect ( evt : Event ) : void
		{
			fileRef.load ( ) ;
			fileRef.addEventListener ( Event.COMPLETE, onDataLoaded ) ;
			//_browseTxt.text = String ( evt.target.name ) ;
		}
		
		private function onDataLoaded ( evt : Event ) : void
		{
			var tempFileRef : FileReference = FileReference ( evt.target ) ;
			loader = new Loader ( ) ;
			loader.contentLoaderInfo.addEventListener ( Event.COMPLETE, onImageLoaded ) ;
			loader.loadBytes ( tempFileRef.data ) ;
		}
		
		private function onImageLoaded ( evt : Event ) : void
		{
			bitmap = Bitmap ( evt.target.content ) ;
			bitmap.smoothing = true;
			bitmap.x = 0;
			bitmap.y = 0;
			//bitmap.x = 5;
			//bitmap.y = _browseTxt.y + _browseTxt.height + 5;						
			//Resize the image if needed
			if ( bitmap.width > boardWidth || bitmap.height > boardHeight ) {
				resizeBitmap ( bitmap ) ;
			}
			
			//board.addChild( bitmap ) ;						
			boardBitmapData.draw(bitmap);
			
			//record undo bitmap and add to undo stack			
			var undoBuffer:BitmapData = new BitmapData(boardWidth, boardHeight, false);
			undoBuffer.copyPixels(boardBitmapData,undoBuffer.rect,new Point(0,0));
			undoStack.push(undoBuffer);
			if (undoStack.length > numUndoLevels + 1) {
				undoStack.splice(0,1);
			}	
		}
		
		private function resizeBitmap( target : Bitmap ) : void
		{
			if ( target.height > target.width ) {
				target.width = boardWidth;
				target.scaleY = target.scaleX;
			} else if ( target.width >= target.height ) {
				target.height = boardHeight;
				target.scaleX = target.scaleY;
			}

		}
		
		private function handleMagicButton1(evt : MouseEvent) : void
		{
			var x:int;
			var y:int;
			var colour:uint = 0x00000000;
			for (x = 0; x < bitmap.width; x++)
			{
				for (y = 0; y < bitmap.height; y++)
				{
					colour += 1;
					bitmap.bitmapData.floodFill(x, y, colour);
				}
			}
		}
		
		// The user pressed the filter button. First change the visibility
		// of the main buttons, and then display the filter types.
		private function HandleFilterButton(evt : MouseEvent) : void
		{
			menuMode = 1;
			ShowAndHideMenus();
		}
		
		private function HandleFilterCancelButton(evt:MouseEvent):void
		{
			menuMode = 0;
			ShowAndHideMenus();
		}
		
		private function SwapUndoBitmap():void
		{
			var x:int;
			var y:int;
			var colour:uint = 0x00000000;
		    for (x = 0; x < boardBitmap.width; x++)
			{
				for (y = 0; y < boardBitmap.height; y++)
				{
					colour = boardBitmapData.getPixel(x, y);
					boardBitmapData.setPixel(x, y, bitmapUndo.getPixel(x, y));
					bitmapUndo.setPixel(x, y, colour);
				}
			}
		}
		
		private function CopyToUndoBitmap():void
		{
			var x:int;
			var y:int;
		    for (x = 0; x < boardBitmap.width; x++)
			{
				for (y = 0; y < boardBitmap.height; y++)
				{
					bitmapUndo.setPixel(x, y, boardBitmapData.getPixel(x, y));
				}
			}
		}
		
		private function HandleFilterInverseButton(evt : MouseEvent) : void
		{
			menuMode = 0;
			ShowAndHideMenus();
			
			// Store backup in undo buffer.
			CopyToUndoBitmap();
			
			// Get the inverse colours.
			var x:int;
			var y:int;
			var colour:uint = 0x00000000;
			for (x = 0; x < boardBitmap.width; x++)
			{
				for (y = 0; y < boardBitmap.height; y++)
				{
					// Flip RGB values.
					colour = boardBitmapData.getPixel(x, y);
					var alphaValue:uint = colour & 0xFF000000;
					colour = ((~colour) & 0x00FFFFFF) | alphaValue;			
					boardBitmapData.setPixel(x, y, colour);
				}
			}
		}
		
		private function HandleFilterGreyscaleButton(evt:MouseEvent):void
		{
			menuMode = 0;
			ShowAndHideMenus();
			
			// Store backup in undo buffer.
			CopyToUndoBitmap();
			
			// Convert to greyscale.
			var x:int;
			var y:int;
			var colour:uint = 0x00000000;
			for (x = 0; x < boardBitmap.width; x++)
			{
				for (y = 0; y < boardBitmap.height; y++)
				{
					// Flip RGB values.
					colour = boardBitmapData.getPixel(x, y);
					var alphaValue:uint = colour & 0xFF000000;
					var redValue:uint = (colour & 0x00FF0000) >> 16;
					var greenValue:uint = (colour & 0x0000FF00) >> 8;
					var blueValue:uint = (colour & 0x000000FF);
					redValue = redValue * 0.299;
					greenValue = greenValue * 0.587;
					blueValue = blueValue * 0.114;
					var greyValue:uint = redValue + greenValue + blueValue;
					greyValue = (greyValue > 255 ? 255 : greyValue);
					redValue = greyValue << 16;
					greenValue = greyValue << 8;
					blueValue = greyValue;
					
					colour = alphaValue | redValue | greenValue | blueValue;
					
					boardBitmapData.setPixel(x, y, colour);
				}
			}
		}
		
		private function HandleFilterTakeMaxButton(evt:MouseEvent):void
		{
			menuMode = 0;
			ShowAndHideMenus();
			
			// Store backup in undo buffer.
			CopyToUndoBitmap();
			
			// Take the maximum RGB value and cull the others.
			var x:int;
			var y:int;
			var colour:uint = 0x00000000;
			for (x = 0; x < boardBitmap.width; x++)
			{
				for (y = 0; y < boardBitmap.height; y++)
				{
					// Find the largest value.
					colour = boardBitmapData.getPixel(x, y);
					var alphaValue:uint = colour & 0xFF000000;
					var redValue:uint = (colour & 0x00FF0000) >> 16;
					var greenValue:uint = (colour & 0x0000FF00) >> 8;
					var blueValue:uint = (colour & 0x000000FF);
					var usedValue:uint = 0;
					var shift:uint = 0;
					if (redValue > greenValue && redValue > blueValue)
					{
						usedValue = redValue;
						shift = 16;
					}
					else if (greenValue > blueValue)
					{
						usedValue = greenValue;
						shift = 8;
					}
					else
					{
						usedValue = blueValue;
						shift = 0;
					}
					
					colour = alphaValue | (usedValue << shift);
					boardBitmapData.setPixel(x, y, colour);
				}
			}
		}
		
		private function HandleFilterColourShiftButton(evt:MouseEvent):void
		{
			menuMode = 0;
			ShowAndHideMenus();
			
			// Store backup in undo buffer.
			CopyToUndoBitmap();
			
			// Perform a right colour shift.
			var x:int;
			var y:int;
			var colour:uint = 0x00000000;
			for (x = 0; x < boardBitmap.width; x++)
			{
				for (y = 0; y < boardBitmap.height; y++)
				{
					// Find the largest value.
					colour = boardBitmapData.getPixel(x, y);
					var alphaValue:uint = colour & 0xFF000000;
					var redValue:uint = (colour & 0x00FF0000) >> 8;
					var greenValue:uint = (colour & 0x0000FF00) >> 8;
					var blueValue:uint = (colour & 0x000000FF) << 16;
					
					colour = alphaValue | blueValue | redValue | greenValue;
					boardBitmapData.setPixel(x, y, colour);
				}
			}
		}
		
		private function HandleFilterEdgeDetectionButton(evt:MouseEvent):void
		{
			menuMode = 0;
			ShowAndHideMenus();
			
			// Store backup in undo buffer.
			CopyToUndoBitmap();
			
			// Convert to greyscale.
			var x:int;
			var y:int;
			var colour:uint = 0x00000000;
			for (x = 0; x < boardBitmap.width; x++)
			{
				var oldGreyValue:uint = 0;
				for (y = 0; y < boardBitmap.height; y++)
				{
					// Flip RGB values.
					colour = boardBitmapData.getPixel(x, y);
					var alphaValue:uint = colour & 0xFF000000;
					var redValue:uint = (colour & 0x00FF0000) >> 16;
					var greenValue:uint = (colour & 0x0000FF00) >> 8;
					var blueValue:uint = (colour & 0x000000FF);
					redValue = redValue * 0.299;
					greenValue = greenValue * 0.587;
					blueValue = blueValue * 0.114;
					var greyValue:uint = redValue + greenValue + blueValue;
					
					if (y != 0 && (greyValue > oldGreyValue + 30 || greyValue < oldGreyValue - 30))
					{
						oldGreyValue = greyValue;
					}
					else
					{
						oldGreyValue = greyValue;
						colour = 0x00000000;
					}
					
					boardBitmapData.setPixel(x, y, colour);
				}
			}
		}
		
		private function HandleBlurFilterButton(evt:MouseEvent):void {
			menuMode = 0;
			ShowAndHideMenus();
			
			// Store backup in undo buffer.
			CopyToUndoBitmap();
			
			var _blurFilter:BlurFilter = new BlurFilter(  );
			
            for(var i:int = 0; i < 500; i++) {
                boardBitmapData.setPixel(mouseX + Math.random(  ) * 20 - 10,
                             mouseY + Math.random(  ) * 20 - 10,
                             0xffffffff);
            }
            boardBitmap.bitmapData.applyFilter(boardBitmapData, boardBitmapData.rect, new Point(  ), _blurFilter);
        }
		
		private function HandleUndoButton(evt:MouseEvent):void
		{
			//SwapUndoBitmap();								
			if (undoStack.length > 1) {
				boardBitmapData.copyPixels(undoStack[undoStack.length - 2],boardBitmapData.rect,new Point(0,0));
				undoStack.splice(undoStack.length - 1, 1);
			}
			
			tipLayer.graphics.clear();
		}
		
		private function InitHandler(evt : Event) : void
		{
			menuMode = 0;
			ShowAndHideMenus();
		}
		
		private function ShowAndHideMenus() : void
		{
			// Menu modes:
			//   0 - Main
			//   1 - Filters
			
			// Main menu.
			FilterButton.visible = (menuMode == 0);
			UndoButton.visible = (menuMode == 0);
			transitionBox.visible = (menuMode == 0);
			imageStepper.visible = (menuMode == 0);
			StartVideoButton.visible = (menuMode == 0);
			//StopVideoButton.visible = (menuMode == 0);
			ScaleSlider.visible = (menuMode == 0);
			AlphaSlider.visible = (menuMode == 0);
			
			// Filter menu.
			FilterGreyscaleButton.visible = (menuMode == 1);
			FilterGreyscaleButton.visible = (menuMode == 1);
			FilterTakeMaxButton.visible = (menuMode == 1);
			FilterInverseButton.visible = (menuMode == 1);
			FilterEdgeDetectionButton.visible = (menuMode == 1);
			FilterColourShiftButton.visible = (menuMode == 1);
			FilterCancelButton.visible = (menuMode == 1);
			CropButton.visible = (menuMode == 1);
			BlurFilterButton.visible = (menuMode == 1);
		}
		
		private function startDraw(evt:MouseEvent):void {
			stage.addEventListener(MouseEvent.MOUSE_UP, stopDraw);
			
			startX = lastMouseX = smoothedMouseX = lastSmoothedMouseX = bitmapHolder.mouseX;
			startY = lastMouseY = smoothedMouseY = lastSmoothedMouseY = bitmapHolder.mouseY;
			lastThickness = 0;
			lastRotation = Math.PI/2;
			colorLevel = 0;
			lastMouseChangeVectorX = 0;
			lastMouseChangeVectorY = 0;
			
			//We will keep track of whether the mouse moves in between a mouse down and a mouse up.  If not,
			//a small dot will be drawn.
			mouseMoved = false;
			
			stage.addEventListener(MouseEvent.MOUSE_MOVE, drawLine);
			//this.addEventListener(Event.ENTER_FRAME, drawLine);
		}
		
		function drawLine(evt:MouseEvent):void {
			mouseMoved = true;
			
			lineLayer.graphics.clear();
			
			mouseChangeVectorX = bitmapHolder.mouseX - lastMouseX;
			mouseChangeVectorY = bitmapHolder.mouseY - lastMouseY;
			
			
			//Cusp detection - if the mouse movement is more than 90 degrees
			//from the last motion, we will draw all the way out to the last
			//mouse position before proceeding.  We handle this by drawing the
			//previous tipLayer, and resetting the last smoothed mouse position
			//to the last actual mouse position.
			//We use a dot product to determine whether the mouse movement is
			//more than 90 degrees from the last motion.
			if (mouseChangeVectorX*lastMouseChangeVectorX + mouseChangeVectorY*lastMouseChangeVectorY < 0) {
				boardBitmapData.draw(tipLayer);
				smoothedMouseX = lastSmoothedMouseX = lastMouseX;
				smoothedMouseY = lastSmoothedMouseY = lastMouseY;
				lastRotation += Math.PI;
				lastThickness = tipTaperFactor*lastThickness;
			}
			
			
			//We smooth out the mouse position.  The drawn line will not extend to the current mouse position; instead
			//it will be drawn only a portion of the way towards the current mouse position.  This creates a nice
			//smoothing effect.
			smoothedMouseX = smoothedMouseX + smoothingFactor*(bitmapHolder.mouseX - smoothedMouseX);
			smoothedMouseY = smoothedMouseY + smoothingFactor*(bitmapHolder.mouseY - smoothedMouseY);
			
			//We determine how far the mouse moved since the last position.  We use this distance to change
			//the thickness and brightness of the line.
			dx = smoothedMouseX - lastSmoothedMouseX;
			dy = smoothedMouseY - lastSmoothedMouseY;
			dist = Math.sqrt(dx*dx + dy*dy);
				
			if (dist != 0) {
				lineRotation = Math.PI/2 + Math.atan2(dy,dx);
			}
			else {
				lineRotation = 0;
			}
			
			//We use a similar smoothing technique to change the thickness of the line, so that it doesn't
			//change too abruptly.
			targetLineThickness = minThickness+thicknessFactor*dist;
			lineThickness = lastThickness + thicknessSmoothingFactor*(targetLineThickness - lastThickness);
			
			/*
			The "line" being drawn is actually composed of filled in shapes.  This is what allows
			us to create a varying thickness of the line.
			*/
			sin0 = Math.sin(lastRotation);
			cos0 = Math.cos(lastRotation);
			sin1 = Math.sin(lineRotation);
			cos1 = Math.cos(lineRotation);
			L0Sin0 = lastThickness*sin0;
			L0Cos0 = lastThickness*cos0;
			L1Sin1 = lineThickness*sin1;
			L1Cos1 = lineThickness*cos1;
			targetColorLevel = Math.min(1,colorChangeRate*dist);
			colorLevel = colorLevel + 0.2*(targetColorLevel - colorLevel);
			
			red = paintColorR1 + colorLevel*(paintColorR2 - paintColorR1);
			green = paintColorG1 + colorLevel*(paintColorG2  - paintColorG1);
			blue = paintColorB1 + colorLevel*(paintColorB2 - paintColorB1);
			
			lineColor = (red << 16) | (green << 8) | (blue);
			
			controlVecX = 0.33*dist*sin0;
			controlVecY = -0.33*dist*cos0;
			controlX1 = lastSmoothedMouseX + L0Cos0 + controlVecX;
			controlY1 = lastSmoothedMouseY + L0Sin0 + controlVecY;
			controlX2 = lastSmoothedMouseX - L0Cos0 + controlVecX;
			controlY2 = lastSmoothedMouseY - L0Sin0 + controlVecY;
			
			lineLayer.graphics.lineStyle(1,lineColor);
			lineLayer.graphics.beginFill(lineColor);
			lineLayer.graphics.moveTo(lastSmoothedMouseX + L0Cos0, lastSmoothedMouseY + L0Sin0);
			lineLayer.graphics.curveTo(controlX1,controlY1,smoothedMouseX + L1Cos1, smoothedMouseY + L1Sin1);
			lineLayer.graphics.lineTo(smoothedMouseX - L1Cos1, smoothedMouseY - L1Sin1);
			lineLayer.graphics.curveTo(controlX2, controlY2, lastSmoothedMouseX - L0Cos0, lastSmoothedMouseY - L0Sin0);
			lineLayer.graphics.lineTo(lastSmoothedMouseX + L0Cos0, lastSmoothedMouseY + L0Sin0);
			lineLayer.graphics.endFill();
			boardBitmapData.draw(lineLayer);
			
			//We draw the tip, which completes the line from the smoothed mouse position to the actual mouse position.
			//We won't actually add this to the drawn bitmap until a mouse up completes the drawing of the current line.
			
			//round tip:
			var taperThickness:Number = tipTaperFactor*lineThickness;
			tipLayer.graphics.clear();
			tipLayer.graphics.beginFill(lineColor);
			tipLayer.graphics.drawEllipse(bitmapHolder.mouseX - taperThickness, bitmapHolder.mouseY - taperThickness, 2*taperThickness, 2*taperThickness);
			tipLayer.graphics.endFill();
			//quad segment
			tipLayer.graphics.lineStyle(1,lineColor);
			tipLayer.graphics.beginFill(lineColor);
			tipLayer.graphics.moveTo(smoothedMouseX + L1Cos1, smoothedMouseY + L1Sin1);
			tipLayer.graphics.lineTo(bitmapHolder.mouseX + tipTaperFactor*L1Cos1, bitmapHolder.mouseY + tipTaperFactor*L1Sin1);
			tipLayer.graphics.lineTo(bitmapHolder.mouseX - tipTaperFactor*L1Cos1, bitmapHolder.mouseY - tipTaperFactor*L1Sin1);
			tipLayer.graphics.lineTo(smoothedMouseX - L1Cos1, smoothedMouseY - L1Sin1);
			tipLayer.graphics.lineTo(smoothedMouseX + L1Cos1, smoothedMouseY + L1Sin1);
			tipLayer.graphics.endFill();
			
			lastSmoothedMouseX = smoothedMouseX;
			lastSmoothedMouseY = smoothedMouseY;
			lastRotation = lineRotation;
			lastThickness = lineThickness;
			lastMouseChangeVectorX = mouseChangeVectorX;
			lastMouseChangeVectorY = mouseChangeVectorY;
			lastMouseX = bitmapHolder.mouseX;
			lastMouseY = bitmapHolder.mouseY;
			
			evt.updateAfterEvent();
			
		}
		
		function stopDraw(evt:MouseEvent):void {
			//If the mouse didn't move, we will draw just a dot.  Its size will be randomized.
			if (!mouseMoved) {
				var randRadius = dotRadius*(0.75+0.75*Math.random());
				var dotColor:uint = (paintColorR1 << 16) | (paintColorG1 << 8) | (paintColorB1);
				var dot:Sprite = new Sprite();
				dot.graphics.beginFill(dotColor)
				dot.graphics.drawEllipse(startX - randRadius, startY - randRadius, 2*randRadius, 2*randRadius);
				dot.graphics.endFill();
				boardBitmapData.draw(dot);
			}
			
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, drawLine);
			stage.removeEventListener(MouseEvent.MOUSE_UP, stopDraw);
				
			//We add the tipLayer to complete the line all the way to the current mouse position:
			boardBitmapData.draw(tipLayer);
			
			//record undo bitmap and add to undo stack			
			var undoBuffer:BitmapData = new BitmapData(boardWidth, boardHeight, false);
			undoBuffer.copyPixels(boardBitmapData,undoBuffer.rect,new Point(0,0));
			undoStack.push(undoBuffer);
			if (undoStack.length > numUndoLevels + 1) {
				undoStack.splice(0,1);
			}			
		}
		
		function scaleChangeHandler(event:SliderEvent):void { 
			//valueLabel.text = "" + event.value;  
			//var prevX:Number = board.X;
			//var prevY:Number = board.Y;
			scaleValue = event.value;
			board.scaleX = event.value;
			board.scaleY = event.value;

			//board.X = prevX;
			//board.Y = prevY;
			//if (bitmap)
				//scaleBitmap(bitmap, event.value);			
			//scaleBitmap(boardBitmap, event.value);
		}
		
		function alphaChangeHandler(event:SliderEvent):void {  
			board.alpha = event.value;
		}
		
		function scaleBitmap(originalBitmap:Bitmap, scale:Number) {
			var originalBitmapData:BitmapData = originalBitmap.bitmapData;
			var scaleFactor:Number = scale;
			var newWidth:Number = originalBitmapData.width*scaleFactor;
			var newHeight:Number = originalBitmapData.height*scaleFactor;
			var scaledBitmapData:BitmapData = new BitmapData(newWidth,newHeight,true,0xFFFFFFFF);
			var scaleMatrix:Matrix = new Matrix();
			scaleMatrix.scale(scaleFactor,scaleFactor);
			scaledBitmapData.draw(originalBitmapData,scaleMatrix);
			originalBitmap.bitmapData=scaledBitmapData;						
		}
		
		private function CropTool ( evt : MouseEvent ) : void
		{			
			// you'll want to make sure the child is added above everything
			// else, possibly in its own container
			myCursor = new CropCursor();			
			//addChild(myCursor);
	
			stage.addEventListener(MouseEvent.MOUSE_MOVE,follow);
						
			/* Quit active tool */

			quitActiveTool();
			
			// you'll want to make sure the child is added above everything
			// else, possibly in its own container
			//myCursor = new EraserCursor();			
			//addChild(myCursor);	
			//stage.addEventListener(MouseEvent.MOUSE_MOVE,follow);

			/* Set to Active */

			active = "Crop";

			/* Listeners */

			stage.addEventListener(MouseEvent.MOUSE_DOWN, setAnchor); 

			/* Highlight */

			highlightTool(crop);
			hideTools(pencil, brush);						
			hideTools(browser, eraser);
			hideTools(txt, eyedrop);
			
			
		}
				
		private function EyeDropTool ( evt : MouseEvent ) : void
		{			
			// you'll want to make sure the child is added above everything
			// else, possibly in its own container
			myCursor = new EyeDropCursor();			
	
			stage.addEventListener(MouseEvent.MOUSE_MOVE,follow);
			
			quitActiveTool();
			board.addEventListener(MouseEvent.CLICK, EyeDropClicked);
			
			active = "EyeDrop";
			
			/* Highlight */

			highlightTool(eyedrop);
			eraser.visible = false;
			hideTools(pencil, brush);						
			hideTools(browser, txt);
			
		}
		
		private function EyeDropClicked(e:MouseEvent):void
		{
			var aPixel:uint = boardBitmapData.getPixel(this.mouseX,this.mouseY);					
			valueLabel.text = "" + aPixel.toString(16);			
			
			var color_transform:ColorTransform=eyeColor.transform.colorTransform;
			color_transform.color=aPixel;			
			eyeColor.transform.colorTransform = color_transform;
			
			activeColor = aPixel;
		}
		
		function setAnchor(e:MouseEvent):void{ 
			if(!_dragging){ 							
				//croppingRect.graphics.clear(); 
				_corner = new Point(e.stageX, e.stageY); 
				_dragging = true; 
				board.addEventListener(MouseEvent.MOUSE_MOVE, liveDrag); 
				 
			} 
		} 
		
		function completeRect(e:MouseEvent):void{ 
			if(_dragging){      
				_dragging = false;          
				board.removeEventListener(MouseEvent.MOUSE_MOVE, liveDrag); 
				croppingRect.removeEventListener(MouseEvent.MOUSE_UP, completeRect); 
				croppingRect.graphics.lineStyle(2, 0x000000); 
				croppingRect.graphics.beginFill(0x222222, .1); 
				croppingRect.graphics.drawRect(_corner.x, _corner.y, e.stageX - _corner.x, e.stageY - _corner.y);
				//board.addChild(croppingRect); 
				//board.addEventListener(Event.COMPLETE, completeCrop);
				completeCrop(e);
			} 
		} 
				
		function liveDrag(e:MouseEvent):void{ 
			croppingRect.graphics.clear(); 
			croppingRect.graphics.lineStyle(2, 0x000000); 							
			if (board.hitTestPoint(_corner.x, _corner.y, true) && board.hitTestPoint(e.stageX, e.stageY, true))
			{				
				croppingRect.graphics.drawRect(_corner.x, _corner.y, e.stageX - _corner.x, e.stageY - _corner.y);
				croppingRect.addEventListener(MouseEvent.MOUSE_UP, completeRect); 
				board.addChild(croppingRect); 
			}
			else {
				_dragging = false;
				board.removeEventListener(MouseEvent.MOUSE_MOVE, liveDrag); 
				croppingRect.removeEventListener(MouseEvent.MOUSE_UP, completeRect); 
			}
	
				 
		} 
		
		function completeCrop(e:MouseEvent):void{ 
			board.graphics.clear();
			
			//define cropping area
			var originalBMP:Bitmap = new Bitmap(boardBitmapData);	
			var rect:Rectangle = croppingRect.getBounds(originalBMP);
			
			//collect bitmap data from myImage			
			var bmd:BitmapData = new BitmapData(rect.width, rect.height, true, 0x000000);			
			bmd.draw(originalBMP, new Matrix(1,0,0,1, -rect.x, -rect.y));

			//record undo bitmap and add to undo stack			
			var undoBuffer:BitmapData = new BitmapData(boardWidth, boardHeight, false);
			undoBuffer.copyPixels(boardBitmapData,undoBuffer.rect,new Point(0,0));
			undoStack.push(undoBuffer);
			if (undoStack.length > numUndoLevels + 1) {
				undoStack.splice(0,1);
			}	
			
			//create bitmap instance to hold bitmap data			
			//and add to the stage			
			clearBoard(e);
			var bmp:Bitmap = new Bitmap(bmd);			
			
			if (_corner.x <= e.stageX)
			{
				bmp.x = _corner.x;
				
				if (_corner.y <= e.stageY)
					bmp.y = _corner.y;
				else
					bmp.y = e.stageY;
			}
			else
			{
				bmp.x = e.stageX;
				
				if (_corner.y <= e.stageY)
					bmp.y = _corner.y;
				else
					bmp.y = e.stageY;
			}					
			
			//boardBitmap = bmp;
			//board.addChild(bmp);			
			boardBitmapData.draw(bmp.bitmapData);			
			croppingRect.graphics.clear();				
		}		

		function dragPressed(event:MouseEvent):void {
			var item:MovieClip = MovieClip(event.target);
			item.startDrag();
			//var topPos:uint = this.numChildren - 1;
			//this.setChildIndex(item, topPos);			
		}
		
		function dragReleased(event:MouseEvent):void {
			var item:MovieClip = MovieClip(event.target);
			item.stopDrag();
		}
		
		private function HandleImageStepper(event:Event):void
		{
		    bitmapHolder.removeChild(boardBitmap);
			boardBitmapData = bitmapArray[imageStepper.value];
			boardBitmap = new Bitmap(boardBitmapData);
			bitmapHolder.addChild(boardBitmap);
			
			// Change the transition data.
			transitionBox.selectedIndex = transitionArray[imageStepper.value];
			if (maxSeenBitmap < imageStepper.value)
			{
				maxSeenBitmap = imageStepper.value;
			}
		}
		
		private function HandleTransitionBox(event:Event):void
		{
			// Change the transition data.
			transitionArray[imageStepper.value] = transitionBox.selectedIndex;
		}
		
		private function TransitionStatic(bitmapDataInput:BitmapData, frame:Number, numFrames:Number):BitmapData
		{
			var bitmapData:BitmapData = bitmapDataInput.clone();
			var x:int;
			var y:int;
			var colour:uint = 0x00000000;
			for (x = 0; x < bitmapData.width; x++)
			{
				for (y = 0; y < bitmapData.height; y++)
				{
					colour += Math.random() * 0xFFFFFFFF;
					bitmapData.setPixel(x, y, colour);
				}
			}
			
			return bitmapData;
		}
		
		private function TransitionWipe(firstBitmap:BitmapData, secondBitmap:BitmapData, frame:Number, numFrames:Number):BitmapData
		{
			// Simple wipe from left to right.
			var bitmapData:BitmapData = secondBitmap.clone();
			var x:int;
			var y:int;
			var startX:int = (frame / (numFrames - 1)) * bitmapData.width;
			var colour:uint = 0x00000000;
			for (x = startX; x < bitmapData.width; x++)
			{
				for (y = 0; y < bitmapData.height; y++)
				{
					colour = firstBitmap.getPixel(x, y);
					bitmapData.setPixel(x, y, colour);
				}
			}
			
			return bitmapData;
		}
		
        private function TransitionWipeUp(firstBitmap:BitmapData, secondBitmap:BitmapData, frame:Number, numFrames:Number):BitmapData
		{
			// Simple wipe from left to right.
			var bitmapData:BitmapData = secondBitmap.clone();
			var x:int;
			var y:int;
			var startY:int = (frame / (numFrames - 1)) * bitmapData.height;
			var colour:uint = 0x00000000;
			for (x = 0; x < bitmapData.width; x++)
			{
				for (y = startY; y < bitmapData.height; y++)
				{
					colour = firstBitmap.getPixel(x, y);
					bitmapData.setPixel(x, y, colour);
				}
			}
			
			return bitmapData;
		}
		
		private function HandlePlayButton(event:MouseEvent):void
		{
			var numberOfImages:int = maxSeenBitmap;
		    var videoTimer:Timer = new Timer(33, numberOfImages * 90); // Temp hack: 9 bitmaps, 90 frames each.
			currentBitmap = 1;
			videoTimer.addEventListener(TimerEvent.TIMER, AnimateBitmaps);
            
			// Generate the "transitions".
			tempVarForTransitions = 0;
			
			// Show the "first" bitmap.
            bitmapHolder.removeChild(boardBitmap);
			boardBitmapData = bitmapArray[currentBitmap];
			boardBitmap = new Bitmap(boardBitmapData);
			bitmapHolder.addChild(boardBitmap);
			videoStopped = false;
			
			videoTimer.start();
		}
		
		private function HandleStopButton(event:MouseEvent):void
		{
			videoStopped = true;
			
			// Show the "first" bitmap.
            bitmapHolder.removeChild(boardBitmap);
			boardBitmapData = bitmapArray[imageStepper.value];
			boardBitmap = new Bitmap(boardBitmapData);
			bitmapHolder.addChild(boardBitmap);
		}
		
		private function AnimateBitmaps(event:TimerEvent):void 
		{
			// For now, this function cycles through bitmaps 1 to 9.
			// Between each, it loads new bitmaps that represent
			// transitions. Functionality should be generalized and
			// made more robust.
			
			// Temp hack: Transition var will go from 0 to 89. If it is
			// between 0 and 59, then do nothing, just show the current image.
			// If it is between 60 and 89, then show static (e.g. transition).
			// If it is over 89, make it 0, and increment the image.
			
			// Check if stop button was pressed.
			if (videoStopped)
			{
			    return;
			}
			
			tempVarForTransitions++;
			if (tempVarForTransitions >= 90)
			{
				// Check for last bitmap.
				if (currentBitmap < maxSeenBitmap)
				{
					currentBitmap++;
					tempVarForTransitions = 0;
				
					// Show next bitmap.
					bitmapHolder.removeChild(boardBitmap);
					boardBitmapData = bitmapArray[currentBitmap];
					boardBitmap = new Bitmap(boardBitmapData);
					bitmapHolder.addChild(boardBitmap);
				}
				else
				{
					// Last call--restore first image.
					bitmapHolder.removeChild(boardBitmap);
					boardBitmapData = bitmapArray[imageStepper.value];
					boardBitmap = new Bitmap(boardBitmapData);
					bitmapHolder.addChild(boardBitmap);
				}
			}
			else if (tempVarForTransitions >= 60)
			{
				// Show "transition".
				if (currentBitmap < maxSeenBitmap)
			    {
					bitmapHolder.removeChild(boardBitmap);
					if (transitionArray[currentBitmap] == 1)
					{
						boardBitmapData = TransitionStatic(bitmapArray[currentBitmap], 1, 2);
					}
					else if (transitionArray[currentBitmap] == 2)
					{
						boardBitmapData = TransitionWipe(bitmapArray[currentBitmap], bitmapArray[currentBitmap + 1], tempVarForTransitions - 60, 30);
					}
					else if (transitionArray[currentBitmap] == 3)
					{
						boardBitmapData = TransitionWipeUp(bitmapArray[currentBitmap], bitmapArray[currentBitmap + 1], tempVarForTransitions - 60, 30);
					}
					boardBitmap = new Bitmap(boardBitmapData);
					bitmapHolder.addChild(boardBitmap);
				}
			}
		}
		
		// soon to be added
		function drawBackground():void {
			//We draw a background with a very subtle gradient effect so that the canvas darkens towards the edges.
			var gradMat:Matrix = new Matrix();
			gradMat.createGradientBox(700,500,0,0,0);
			var bg:Sprite = new Sprite();
			bg.graphics.beginGradientFill("radial",[0xDDD0AA,0xC6B689],[1,1],[1,255],gradMat);
			bg.graphics.drawRect(0,0,700,500);
			bg.graphics.endFill();
			boardBitmapData.draw(bg);
			
			//We clear out the undo buffer with a copy of just a blank background:
			var undoBuffer:BitmapData = new BitmapData(boardWidth, boardHeight, false);
			undoBuffer.copyPixels(boardBitmapData,undoBuffer.rect,new Point(0,0));
			undoStack.push(undoBuffer);
		}
		
		private function addListeners():void
		{
			pencilTool.addEventListener(MouseEvent.MOUSE_UP, PencilTool);
			eraserTool.addEventListener(MouseEvent.MOUSE_UP, EraserTool);
			textTool.addEventListener(MouseEvent.MOUSE_UP, TextTool);
			saveButton.addEventListener(MouseEvent.MOUSE_UP, save);
			clearTool.addEventListener(MouseEvent.MOUSE_UP, clearBoard);
			colors.addEventListener(MouseEvent.MOUSE_UP, chooseColor);
			sizePanel.addEventListener(MouseEvent.MOUSE_UP, changeShapeSize);
			shapeSize.addEventListener(MouseEvent.MOUSE_UP, changeShapeSize);
			browserTool.addEventListener ( MouseEvent.MOUSE_UP, BrowserTool );
			brushTool.addEventListener(MouseEvent.MOUSE_UP, BrushTool);
			FilterButton.addEventListener(MouseEvent.CLICK, HandleFilterButton);
			FilterCancelButton.addEventListener(MouseEvent.CLICK, HandleFilterCancelButton);
			FilterInverseButton.addEventListener(MouseEvent.CLICK, HandleFilterInverseButton);
			FilterGreyscaleButton.addEventListener(MouseEvent.CLICK, HandleFilterGreyscaleButton);
			FilterTakeMaxButton.addEventListener(MouseEvent.CLICK, HandleFilterTakeMaxButton);
			FilterColourShiftButton.addEventListener(MouseEvent.CLICK, HandleFilterColourShiftButton);
			FilterEdgeDetectionButton.addEventListener(MouseEvent.CLICK, HandleFilterEdgeDetectionButton);
			UndoButton.addEventListener(MouseEvent.CLICK, HandleUndoButton);
			//bitmapHolder.addEventListener(MouseEvent.MOUSE_DOWN, startDraw);
			ScaleSlider.addEventListener(SliderEvent.CHANGE, scaleChangeHandler); 
			AlphaSlider.addEventListener(SliderEvent.CHANGE, alphaChangeHandler); 
			BlurFilterButton.addEventListener(MouseEvent.CLICK, HandleBlurFilterButton);
			cropTool.addEventListener(MouseEvent.MOUSE_UP, CropTool);
			eyeDropTool.addEventListener(MouseEvent.MOUSE_UP, EyeDropTool);
			//toolsGroup.outerTools.addEventListener(MouseEvent.MOUSE_DOWN, dragPressed);
			//toolsGroup.outerTools.addEventListener(MouseEvent.MOUSE_UP, dragReleased);
			imageStepper.addEventListener(Event.CHANGE, HandleImageStepper);
			StartVideoButton.addEventListener(MouseEvent.CLICK, HandlePlayButton);
			//StopVideoButton.addEventListener(MouseEvent.CLICK, HandleStopButton);
			transitionBox.addEventListener(Event.CHANGE, HandleTransitionBox);
			loaderInfo.addEventListener(Event.INIT, InitHandler);
		}
	}
}